#include "alengine.h"
#include "utils/engine_error.h"
#include <unistd.h>

#define TIMER_INTERVAL 355000

void sound_thrd::run()
{
    if (!engine_caller)
        return;
    std::set<ptr_t> *t = (std::set<ptr_t>*)ptr;
    while (!is_stopped() && (!t->empty()))
    {
        for (std::set<ptr_t>::iterator it = t->begin(); (!t->empty())&&(it != t->end()); it++)
        {
            Sound *snd = engine_caller->ptr_cast(*it);
            if (snd)
                snd->update();
        }
        usleep(TIMER_INTERVAL);
    }
    stop();
}

//------------UTILS
Sound * oal_engine::ptr_cast(ptr_t channel)
{
    Sound *sound = static_cast<Sound *> ((void *)channel);
    if (snd_map.find(channel) != snd_map.end())
        return sound;
    else
        return NULL;
}

double oal_engine::channel_bytes2seconds(ptr_t hndl, QWORD bytes)
{
    if (snd_map.find(hndl) != snd_map.end())
    {
        SoundData *snd_data = snd_map[hndl];
        return snd_data->bytes2secs(bytes);
    }
    else
        return 0.0;
}

QWORD oal_engine::channel_seconds2bytes(ptr_t hndl, double seconds)
{
    if (snd_map.find(hndl) != snd_map.end())
    {
        SoundData *snd_data = snd_map[hndl];
        return snd_data->secs2bytes(seconds);
    }
    else
        return 0;
}



void oal_engine::Apply3D(float x, float y, float z)
{
    Vector3D t(x, y, z);
    context.moveTo(t);
}

oal_engine::oal_engine()
{
    snd_thread = NULL;
    curr_hndl = 0;
}

void oal_engine::term_wrapper(std::pair<ptr_t, SoundData *> elem)
{
    terminate_channel(elem.first);
}

oal_engine::~oal_engine()
{
    std::map<ptr_t, SoundData *>::iterator it = snd_map.begin();
    while (!snd_map.empty() && (it != snd_map.end()))
    {
        terminate_channel((*it).first);
        ++it;
    }
    if (curr_hndl)
    {
        Sound *snd = ptr_cast(curr_hndl);
        if (snd)
            delete snd;
    }
    if (snd_thread)
    {
        if (!snd_thread->is_stopped())
            snd_thread->stop();
        delete snd_thread;
    }
}

float oal_engine::channel_get_volume(ptr_t channel)
{
    Sound *sound = ptr_cast(channel);
    if (sound)
        return sound->getGain() * 100.0;
    else
    {
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, "in get_volume");
        return -1;
    }
}

void oal_engine::channel_set_volume(ptr_t channel, float volume)
{
    Sound *sound = ptr_cast(channel);
    if (sound)
    {
        sound->setGain(volume / 100.0);
    }
    else
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, "in set_volume");
}

void oal_engine::terminate_channel(ptr_t channel)
{
    Sound *sound = ptr_cast(channel);
    if (sound)
    {
        sound->stop();
        snd_map.erase(channel);
        snd_played.erase(channel);
       // context.unregisterSound(sound);
        usleep(2 * TIMER_INTERVAL);
        delete sound;
    }
    else
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, " in Terminate Request");
}

void oal_engine::pause_channel(ptr_t channel)
{
    Sound *sound = ptr_cast(channel);
    if (sound)
    {
        sound->pause();
        snd_played.erase(channel);
    }

}

int oal_engine::channel_get_length(ptr_t channel)
{
    Sound *sound = ptr_cast(channel);
    if (sound && (snd_map.find(channel) != snd_map.end()))
    {
        SoundData * t = snd_map[channel];
        return t->getDuration();
    }
    else
    {
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, " in get_length");
        return 0;
    }
}

QWORD oal_engine::channel_get_pos(ptr_t channel)
{
    Sound *sound = ptr_cast(channel);
    if (sound && (snd_map.find(channel) != snd_map.end()))
    {
        QWORD pos = sound->get_audio_pos();
        return pos;
    }
    else
    {
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, " in get_pos");
        return 0;
    }
}

void oal_engine::channel_set_pos(ptr_t channel, QWORD pos)
{
    Sound *sound = ptr_cast(channel);
    if (sound)
    {
        SoundData * t = snd_map[channel];
        t->seek(t->bytes2secs(pos));
    }
    else
        engine_error(ENGINE_ENGINE_ERROR, ENGINE_EBADHNDL, "set_pos Request with bad Handle");
}


ptr_t oal_engine::create_channel(const char *fname, bool looping, bool playafter)
{
    SoundData * mysnd_data = OpenAlContext::open(fname);
    if (mysnd_data == NULL)
    {
        engine_error(ENGINE_ALC_ERROR, ENGINE_EBADFILE, "Cant open this file");
        return 0;
    }
    Sound *new_snd = new Sound(mysnd_data, looping);
    if (playafter)
    {
         new_snd->play();
         snd_played.insert(QWORD(new_snd));
         if (!snd_thread)
         {
            snd_thread = new sound_thrd;
            snd_thread->ptr = &(snd_played);
            snd_thread->engine_caller = this;
         }
         if (snd_thread->is_stopped())
             /*if (*/snd_thread->start(); /*== 0)
             {
                 engine_error(ENGINE_ENGINE_ERROR, ENGINE_EPTHREADERROR, "Cant start thread");
                 return 0;
             }*/

    }
    snd_map[QWORD(new_snd)] = mysnd_data;
    curr_hndl = QWORD(new_snd);
    return QWORD(new_snd);
}
